﻿using Recipes.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Data.Xml.Dom;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.Web.Syndication;

namespace Recipes.Logic
{
    /// <summary>
    /// Classe che consente di effettuare il download delle ricette
    /// </summary>
    public class RecipeDownloader
    {
        //Indirizzo di base del servizio da cui si scaricano le ricette
        private readonly string BASEURL = "http://www.4yougratis.it/ricette_cucina/";
        //Suffisso che indica che si desidera scaricare il feed rss
        private readonly string RSS_SFX = "rss.xml";


        public async Task<IList<Recipe>> ReadFeedFromFile(Category category)
        {
            try
            {
                var folder = Package.Current.InstalledLocation;
                folder = await folder.GetFolderAsync("Assets");
                folder = await folder.GetFolderAsync("Feed");

                var file = await folder.GetFileAsync(string.Format("{0}Recipes.xml", category.ToString()));

                string text = await FileIO.ReadTextAsync(file);

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(text);

                SyndicationFeed feed = new SyndicationFeed();
                feed.LoadFromXml(xmlDoc);

                IList<Recipe> toRet = new List<Recipe>();
                foreach (var f in feed.Items) //Per ogni feed presente nel RSS scaricato
                {

                    Recipe r = new Recipe()
                    {
                        Guid = Guid.NewGuid(),
                        Title = f.Title.Text,
                        Category = f.Categories.FirstOrDefault() != null ? f.Categories.FirstOrDefault().Term : string.Empty,
                        FeedCategory = category,
                        PublishDate = f.PublishedDate,
                        Summary = f.Summary.Text,
                        Link = f.Links.FirstOrDefault() != null ? f.Links.FirstOrDefault().Uri : null
                    };

                    //Aggiungo la ricetta alla lista da restituire.
                    toRet.Add(r);

                }

                return toRet;

            }
            catch (Exception ex)
            {
                //In caso di errore, scrivo in console cosa è successo
                Debug.WriteLine(ex.Message);
                //rilancio l'eccezione per consentire al chiamante di gestirla.
                throw ex;
            }
        }

        /// <summary>
        /// Questo metodo effettua il download di un elenco di ricette
        /// </summary>
        /// <param name="category">La categoria di ricette da scaricare</param>
        /// <returns></returns>
        public async Task<IList<Recipe>> DownloadRecipe(Category category)
        {

            //Costruisce l'url da cui scaricare le ricette
            string param = "";
            if (category != Category.New)
                param = String.Format("{0}/", category.ToString());
            string uri = String.Format("{0}{1}{2}", BASEURL, param, RSS_SFX);

            //Racchiudo il codice di scaricamento in un blocco Try - Catch
            //Se vengono sollevate eccezioni per qualche problema (connettività, errori di parsing, ...)
            //il blocco catch li raccoglie, impedendo all'app di arrestarsi.
            try
            {
                //Scarica le ricette
                SyndicationClient client = new SyndicationClient();                
                SyndicationFeed feed = await client.RetrieveFeedAsync(new Uri(uri));

                //Per ogni feed RSS, recupero le informazioni che mi interessano
                //e costruisco un modello aderente alla logica della mia applicazione (Model).
                IList<Recipe> toRet = new List<Recipe>();
                foreach (var f in feed.Items) //Per ogni feed presente nel RSS scaricato
                {
                    
                    Recipe r = new Recipe()
                    {
                        Guid = Guid.NewGuid(),
                        Title = f.Title.Text,
                        Category = f.Categories.FirstOrDefault() != null ? f.Categories.FirstOrDefault().Term : string.Empty,
                        FeedCategory = category,
                        PublishDate = f.PublishedDate,
                        Summary = f.Summary.Text,
                        Link = f.Links.FirstOrDefault() != null ? f.Links.FirstOrDefault().Uri : null
                    };

                    //Aggiungo la ricetta alla lista da restituire.
                    toRet.Add(r);

                }

                return toRet;

            }
            catch (Exception ex)
            {
                //In caso di errore, scrivo in console cosa è successo
                Debug.WriteLine(ex.Message);
                //rilancio l'eccezione per consentire al chiamante di gestirla.
                throw ex;
            }

            #region Printing
            /* DEBUG: Stampa nella console alcune caratteristiche dei feed
            foreach (var f in feed.Items)
            {
                Debug.WriteLine(string.Format("TITLE\n{0}\n", f.Title.Text));
                Debug.WriteLine(string.Format("CATEGORY\n{0}\n", f.Categories.First().Term));
                Debug.WriteLine(string.Format("PUBLISHDATE\n{0}\n", f.PublishedDate));
                Debug.WriteLine(string.Format("SUMMARY\n{0}\n", f.Summary.Text));
                Debug.WriteLine(string.Format("LINK\n{0}\n", f.Links.First().Uri));
                
            }
             * */
            #endregion
        }


        /// <summary>
        /// Metodo che scarica tutte le ricette di tutte le categorie
        /// </summary>
        /// <returns>La lista di ricette scaricate</returns>
        public async Task<IList<Recipe>> DownloadAllRecipes()
        {            
            //Raccolgo le ricette scaricate in un dizionario dove
            //la chiave è la categoria
            //il valore è la lista delle ricette di quella categoria
            IDictionary<string, IList<Recipe>> allRecipes = new Dictionary<string, IList<Recipe>>();

            try
            {
                //Preparo la lista delle categorie
                IList<Category> list = new List<Category>();
                foreach (var c in Enum.GetValues(typeof(Category)))
                    list.Add((Category)c);

                //Effettuo lo scaricamento in parallelo delle notizie.

                foreach (Category c in list)
                {
                    //Invoco il metodo di scaricamento sulla categoria c
                    IList<Recipe> recipes = await DownloadRecipe(c);
                    //Aggiungo tutti gli elementi scaricati al dizionario
                    allRecipes.Add(c.ToString(), recipes);
                }

                #region Parallel
                //Parallel è la libreria che contiene i metodi per eseguire codice in parallelo
                //ForEach<Category>() è il metodo che consente di iterare su una enumerazione di elementi di tipo Category
                //il primo parametro del metodo è l'enumerazione che contiene tutti gli elementi su cui iterareo i 
                //il secondo parametro è l'azione da eseguire su ciascun elemento. Questa è una lambda-expression.
                /*
                Parallel.ForEach<Category>(list, async (c) =>
                {
                    try
                    {
                        //Invoco il metodo di scaricamento sulla categoria c
                        IList<Recipe> recipes = await DownloadRecipe(c);
                        //Aggiungo tutti gli elementi scaricati al dizionario
                        allRecipes.Add(c.ToString(), recipes);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("Message: "+ex.Message);
                        Debug.WriteLine("Source: "+ex.Source);
                        Debug.WriteLine("StackTrace\n"+ex.StackTrace);

                    }
                });
                 * */
                #endregion
            }
            catch (Exception ex)
            {
                //Raccolgo le eccezioni che sono state lanciate durante l'invocazione parallela
                Debug.WriteLine(ex.Message);
            }

            //A questo punto, nel dizionario ho una struttura del tipo:
            //  Antipasti - {ricetta1,, ricetta2, ...}
            //  Primi Piatti - {ricetta1,, ricetta2, ...}
            //  Secondi Piatti - {ricetta1,, ricetta2, ...}
            //  Dolci - {ricetta1,, ricetta2, ...}
            //  Ultime - {ricetta1,, ricetta2, ...}
            //
            //Devo unirle in un'unica lista per restituirle al chiamante
            IList<Recipe> toRet = new List<Recipe>(); //Creo la lista da restituire
            foreach (string key in allRecipes.Keys) //Per ogni chiave del dizionario
            {                
                foreach (var r in allRecipes[key]) //Per ogni ricetta nell'elenco di quelle scaricate
                    toRet.Add(r); //Aggiungo la ricetta alla lista da restituire
            }

            return toRet;
        }               
    }

}
